(() => {
    interface Row {
        Number: string;
        Privilege: string;
        Name: string;
        Description: string;
    }

    const fillItem = (rows: Row[]): Row[] => {
        // find incomplete item
        const incompleteIdx = rows.findIndex(
            (row) => row.Name.startsWith("⋯") || row.Name.startsWith("⋮")
        );
        if (incompleteIdx === -1) return rows;

        // fill incomplete item
        const startItem = rows[incompleteIdx - 1] as Row;
        const endItem = rows[incompleteIdx + 1] as Row;
        const startNumber = parseInt(startItem.Number, 16);
        const endNumber = parseInt(endItem.Number, 16);

        const diff = endNumber - startNumber;

        const startName = startItem.Name;
        const startNameNumber = parseInt(startName.match(/\d+/)![0]);

        const filledItems: Row[] = [];
        for (let i = 1; i < diff; i++) {
            const filledNumber =
                "0x" + (startNumber + i).toString(16).toUpperCase();
            const filledName = startName.replace(
                startNameNumber.toString(),
                (startNameNumber + i).toString()
            );
            const filledItem = {
                Number: filledNumber,
                Privilege: startItem.Privilege,
                Name: filledName,
                Description: startItem.Description.replace(
                    startName,
                    filledName
                ),
            };
            filledItems.push(filledItem);
        }

        const newRows = [...rows];
        newRows.splice(incompleteIdx, 1, ...filledItems);
        return fillItem(newRows);
    };

    const extractTableRow = (tr: HTMLTableRowElement): string => {
        const tds = tr.querySelectorAll("td");
        const cols = Array.from(tds).map((td) => {
            return td.textContent?.split("\n");
        });
        const transpose = (a: string[][]): string[][] =>
            (a[0] as string[]).map((_, c) => a.map((r) => r[c])) as string[][];
        const transposed = transpose(cols as any);
        const rows = transposed.map((row) => {
            const [Number, Privilege, Name, Description] = row;
            return { Number, Privilege, Name, Description } as Row;
        });

        const filledRows = fillItem(rows);

        const results = filledRows
            .map((row) => {
                return `${row.Name.toUpperCase()} = ${row.Number}, // ${
                    row.Description
                }`;
            })
            .join("\n");

        return results;
    };

    const extractTable = (table: HTMLTableElement) => {
        const trs = table.querySelectorAll(
            "tbody > tr"
        ) as NodeListOf<HTMLTableRowElement>;
        const rows = Array.from(trs)
            .map((tr) => {
                if (tr.children.length === 4) {
                    return extractTableRow(tr);
                } else {
                    return `\n// ${tr.textContent?.trim()}`;
                }
            })
            .join("\n");
        return rows;
    };

    const tableIds = [
        "ucsrnames",
        "scsrnames",
        "hcsrnames",
        "mcsrnames0",
        "mcsrnames1",
    ];

    const results = tableIds
        .map((id) => {
            const table = document.getElementById(id) as HTMLTableElement;
            return extractTable(table);
        })
        .join("\n");

    console.log(results);
})();
